///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file main.cpp
///
/// \brief   contains the main function for the pcps code
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#include <src/pcps.h>
#include <src/mpi_interface.h>
#include <src/blas_lapack.h>
#include <src/timing.h>
#include <src/random.h>
#include <src/all_data.h>
#include <src/command.h>
#include <src/archive.h>
#include <src/davidson.h>
#include <src/cholesky.h>
#include <src/determinants.h>

//template <class T>
//void test_determinant_lemma_derivative(int argc, char ** argv);

template <class S> void test_agp(const pcps::Input & userinp, pcps::Hamiltonian<S> & ham, pcps::Wavefunction<S> & wfn);

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   main function for the pcps code
///
/// \param[in]     argc     number of command line arguments
/// \param[in]     argv     command line arguments
///
/// \return exit status of the pcps program
///
///////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char * argv[]) {

  // start a timer to track the total time taken by the program's execution
  pcps::start_timer("total elapsed time");

  // initialize MPI
  pcps::start_timer("mpi initialization");
  pcps::mpi::init(argc, argv);
  pcps::stop_timer("mpi initialization");

  //pcps::reset_timer("get_value time");
  //pcps::reset_timer("update time");
  //pcps::reset_timer("calculate_a time");
  //pcps::reset_timer("get_ham_slice time");
  //pcps::reset_timer("big contraction time");
  //pcps::reset_timer("diag contraction time");
  //pcps::reset_timer("other calculate_a time");

  // get MPI info
  const int nproc  = pcps::mpi::size();
  const int myrank = pcps::mpi::rank();

  // check that an input file name was supplied as a command line argument
  if (argc != 2)
    throw pcps::Exception("the pcps program requires the input file name as its only argument");

  //// set the random seed
  //pcps::start_timer("setting random seed");
  //pcps::set_seed(userinp.global_seed());
  //pcps::stop_timer("setting random seed");

  //{
  //  pcps::set_seed(std::time(NULL));
  //  test_determinant_lemma_derivative<double>(argc, argv);
  //  test_determinant_lemma_derivative<std::complex<double> >(argc, argv);
  //  return 0;
  //}

  {

    // create an object to hold the user input options, wavefunctions, and hamiltonians
    pcps::AllData data;

    // set the random seed
    pcps::set_seed(38021712);
    pcps::set_seed(std::time(NULL));

    //std::cout << "myrank = " << pcps::mpi::rank() << "   first random number = " << pcps::global_lcg() << std::endl;

    // run commands from the user's input file
    pcps::run_commands<pcps::TextFileArchive>(argv[1], data);

    if ( data.userinp.scalar_type() == pcps::option_value::Scalar_Real )
      test_agp(data.userinp, data.hd, data.wd);
    else if ( data.userinp.scalar_type() == pcps::option_value::Scalar_Complex )
      test_agp(data.userinp, data.hc, data.wc);
    else
      throw pcps::Exception("unknown scalar type %s") % data.userinp.scalar_type();

    //pcps::test_davidson<double>(data.userinp.nsites());
    //pcps::test_davidson<std::complex<double> >(data.userinp.nsites());

    //pcps::test_cholesky<double>(data.userinp.nsites());
    //pcps::test_cholesky<std::complex<double> >(data.userinp.nsites());

    //pcps::test_determinants<double>(data.userinp.max_hf_iter());
    //pcps::test_determinants<std::complex<double> >(data.userinp.max_hf_iter());

  }

  // finalize MPI
  pcps::start_timer("mpi finalization");
  pcps::mpi::finalize();
  pcps::stop_timer("mpi finalization");

  // print timers on root node
  if (myrank == 0)
    pcps::print_timers();

  // return a successful exit code
  return 0;

}
